import router from "./router";
import { ElMessage } from "element-plus";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { getToken } from "@/utils/auth";
import { isHttp } from "@/utils/validate";
import { isRelogin } from "@/utils/request";
import useUserStore from "@/store/modules/user";
import useSettingsStore from "@/store/modules/settings";
import usePermissionStore from "@/store/modules/permission";

NProgress.configure({ showSpinner: false });

const whiteList = [
  "/login",
  "/register",
  "/recordTime",
  "/teacherLogin",
  "/teacherRegister",
];

/**
 * 全局前置路由守卫，在路由跳转前执行
 * @param {Object} to - 即将要进入的目标路由对象
 * @param {Object} from - 当前导航正要离开的路由对象
 * @param {Function} next - 调用该方法来 resolve 这个钩子，控制路由跳转
 */
router.beforeEach((to, from, next) => {
  // 启动进度条
  NProgress.start();

  // 获取 token
  const hasToken = getToken();

  if (hasToken) {
    // 如果路由元信息中存在 title，则设置页面标题
    to.meta.title && useSettingsStore().setTitle(to.meta.title);
    /* 已获取 token */

    // 如果目标路径是登录页，重定向到首页
    if (to.path === "/login") {
      next({ path: "/" });
      // 完成进度条
      NProgress.done();
    }
    // 如果目标路径在白名单中，直接放行
    else if (whiteList.indexOf(to.path) !== -1) {
      next();
    }
    // 其他情况
    else {
      // 判断当前用户是否已获取角色信息
      if (useUserStore().roles.length === 0) {
        // 显示重新登录提示
        isRelogin.show = true;
        // 判断当前用户是否已拉取完 user_info 信息
        useUserStore()
          .getInfo()
          .then(() => {
            // 隐藏重新登录提示
            isRelogin.show = false;
            // 根据角色权限生成可访问的路由表
            usePermissionStore()
              .generateRoutes()
              .then((accessRoutes) => {
                // 遍历可访问路由表
                accessRoutes.forEach((route) => {
                  // 如果路由路径不是 http 链接，则动态添加该路由
                  if (!isHttp(route.path)) {
                    router.addRoute(route); // 动态添加可访问路由表
                  }
                });
                // 使用 hack 方法确保 addRoutes 已完成
                next({ ...to, replace: true });
              });
          })
          .catch((err) => {
            // 登出操作
            useUserStore()
              .logOut()
              .then(() => {
                // 显示错误信息
                ElMessage.error(err);
                // 重定向到首页
                next({ path: "/" });
              });
          });
      } else {
        // 检查是否为后台管理界面，学生用户不能访问
        const userStore = useUserStore();
        const isAdmin = userStore.roles.includes("admin");
        const isAcademic = userStore.roles.includes("academic");
        const isCommon = userStore.roles.includes("common");
        const isAdminPath =
          to.path.startsWith("/system/") ||
          to.path.startsWith("/monitor/") ||
          to.path.startsWith("/tool/") ||
          to.path === "/system" ||
          to.path === "/monitor" ||
          to.path === "/index" ||
          to.path === "/CampusShuttle/bus" ||
          to.path === "/CampusShuttle/schedule" ||
          to.path === "/CampusShuttle/date" ||
          to.path === "/tool";

        if (!isAdmin && !isCommon && isAdminPath && !isAcademic) {
          ElMessage.error("无权访问后台管理界面");
          next({ path: "/login" });
          NProgress.done();
        } else if (
          to.path.startsWith("/CampusBus") &&
          !userStore.roles.includes("teacher")
        ) {
          ElMessage.error("无权访问教师端界面");
          next({ path: "/teacherLogin", query: { redirect: to.fullPath } });
          NProgress.done();
        } else {
          next();
        }
      }
    }
  } else {
    // 没有 token
    // 如果目标路径在免登录白名单中，直接进入
    if (whiteList.indexOf(to.path) !== -1) {
      next();
    } else if (to.path.startsWith("/CampusBus")) {
      next(`/teacherLogin?redirect=${to.fullPath}`);
      NProgress.done();
    } else {
      // 否则全部重定向到登录页，并携带当前路径作为重定向参数
      next(`/login?redirect=${to.fullPath}`);
      // 完成进度条
      NProgress.done();
    }
  }
});

/**
 * 全局后置路由守卫，在路由跳转完成后执行
 * 此守卫的作用是结束进度条的显示，意味着页面跳转操作已完成
 */
router.afterEach(() => {
  // 调用 NProgress.done() 方法来完成进度条的显示，标志路由跳转操作结束
  NProgress.done();
});
